// 服务器端数据管理模块 - 视频数据表的设计

// 在视频共享点播系统中, 视频数据和图片数据都存储在文件中, 而我们需要在数据库中管理用户上传的每个视频信息
// 只是完成一个简单的视频信息表
// 1. 视频ID
// 2. 视频名称
// 3. 视频描述信息
// 4. 视频文件的 url 路径 (加上相对根目录实际上就是实际存储路径)
// 5. 视频封面图片的 URL 路径 (只是链接, 加上相对根目录才是实际的存储路径)

// 如果两个头文件定义了相同的命名空间 aod，并且其中有重复的符号（如相同名字的函数、类、变量等），会导致 重定义错误。
// 如果没有重复的符号，C++ 会合并这些命名空间，但需要小心管理命名冲突。为避免这些问题，最好使用头文件保护、声明与定义分离，并在需要时细分命名空间。

// 这段 SQL 语句定义了一个名为 aod_system 的数据库，并在该数据库中创建了一个名为 tb_video 的表。接下来，我们将逐行分析这段 SQL 语句。
//  drop database if exists aod_system;
//  create database if not exists aod_system;
//  use aod_system;
//  create table if not exists tb_video(
//   id int primary key auto_increment comment '视频ID',
//   name varchar(32) comment '视频名称',
//   info text comment '视频描述',
//   video varchar(256) comment '视频文件url，加上静态资源根目录就是实际存储路径',
//   image varchar(256) comment '封面图片文件url，加上静态资源根目录就是实际存储路径'
//  );

// aod 是一个命名空间，包含了与 MySQL 数据库交互的相关代码和 TableVideo 类。命名空间有助于组织代码，避免名称冲突。
// HOST 是 MySQL 数据库的地址（本地地址 127.0.0.1）。
// USER 是数据库用户名。
// PASS 是数据库密码。
// NAME 是要连接的数据库名称。

#ifndef __MY_DATA__
#define __MY_DATA__
#include "util.hpp"
#include <cstdlib>
#include <mutex>
#include <mysql/mysql.h>

namespace aod
{
#define HOST "127.0.0.1"
#define USER "root"
#define PASS ""
#define NAME "aod_system"
    static MYSQL *MysqlInit()
    {
        MYSQL *mysql = mysql_init(NULL);
        if (mysql == NULL)
        {
            std::cout << "init mysql instance failed!\n";
            return NULL;
        }
        if (mysql_real_connect(mysql, HOST, USER, PASS, NAME, 0, NULL, 0) == NULL)
        {
            std::cout << "connect mysql server failed!\n";
            mysql_close(mysql);
            return NULL;
        }
        mysql_set_character_set(mysql, "utf8");
        return mysql;
    }
    static void MysqlDestroy(MYSQL *mysql)
    {
        if (mysql != NULL)
        {
            mysql_close(mysql);
        }
        return;
    }
    static bool MysqlQuery(MYSQL *mysql, const std::string &sql)
    {
        int ret = mysql_query(mysql, sql.c_str());
        if (ret != 0)
        {
            std::cout << sql << std::endl;
            std::cout << mysql_error(mysql) << std::endl;
            return false;
        }
        return true;
    }
    class TableVideo
    {
    private:
        MYSQL *_mysql;
        std::mutex _mutex;

    public:
        TableVideo()
        {
            _mysql = MysqlInit();
            if (_mysql == NULL)
            {
                exit(-1);
            }
        }
        ~TableVideo()
        {
            MysqlDestroy(_mysql);
        }
        bool Insert(const Json::Value &video)
        {
            // id name info video image
            std::string sql;
            sql.resize(4096 + video["info"].asString().size()); // 防止简介过长
#define INSERT_VIDEO "insert tb_video values(null, '%s', '%s', '%s', '%s');"
            if (video["name"].asString().size() == 0)
            {
                return false;
            }
            // 要完成的细致的话需要对各个数据进行校验，因为不校验直接用就有可能出问题
            sprintf(&sql[0], INSERT_VIDEO, video["name"].asCString(),
                    video["info"].asCString(),
                    video["video"].asCString(),
                    video["image"].asCString());
            return MysqlQuery(_mysql, sql);
        }
        bool Update(int video_id, const Json::Value &video)
        {
            std::string sql;
            sql.resize(4096 + video["info"].asString().size()); // 防止简介过长
#define UPDATE_VIDEO "update tb_video set name='%s', info='%s' where id=%d;"
            sprintf(&sql[0], UPDATE_VIDEO, video["name"].asCString(),
                    video["info"].asCString(), video_id);
            return MysqlQuery(_mysql, sql);
        }
        bool Delete(int video_id)
        {
#define DELETE_VIDEO "delete from tb_video where id=%d;"
            char sql[1024] = {0};
            sprintf(sql, DELETE_VIDEO, video_id);
            return MysqlQuery(_mysql, sql);
        }
        bool SelectAll(Json::Value *videos)
        {
#define SELECTALL_VIDEO "select * from tb_video;"
            _mutex.lock(); //-----lock start 保护查询与保存结果到本地的过程
            bool ret = MysqlQuery(_mysql, SELECTALL_VIDEO);
            if (ret == false)
            {
                _mutex.unlock();
                return false;
            }
            MYSQL_RES *res = mysql_store_result(_mysql);
            if (res == NULL)
            {
                std::cout << "mysql store result failed!\n";
                _mutex.unlock();
                return false;
            }
            _mutex.unlock(); //------lock end
            int num_rows = mysql_num_rows(res);
            for (int i = 0; i < num_rows; i++)
            {
                MYSQL_ROW row = mysql_fetch_row(res);
                Json::Value video;
                video["id"] = atoi(row[0]);
                video["name"] = row[1];
                video["info"] = row[2];
                video["video"] = row[3];
                video["image"] = row[4];
                videos->append(video);
            }
            mysql_free_result(res);
            return true;
        }
        bool SelectOne(int video_id, Json::Value *video)
        {
#define SELECTONE_VIDEO "select * from tb_video where id=%d;"
            char sql[1024] = {0};
            sprintf(sql, SELECTONE_VIDEO, video_id);
            _mutex.lock(); //-----lock start 保护查询与保存结果到本地的过程
            bool ret = MysqlQuery(_mysql, sql);
            if (ret == false)
            {
                _mutex.unlock();
                return false;
            }
            MYSQL_RES *res = mysql_store_result(_mysql);
            if (res == NULL)
            {
                std::cout << "mysql store result failed!\n";
                _mutex.unlock();
                return false;
            }
            _mutex.unlock(); //------lock end
            int num_rows = mysql_num_rows(res);
            if (num_rows != 1)
            {
                std::cout << "have no data!\n";
                mysql_free_result(res);
                return false;
            }
            MYSQL_ROW row = mysql_fetch_row(res);
            (*video)["id"] = video_id;
            (*video)["name"] = row[1];
            (*video)["info"] = row[2];
            (*video)["video"] = row[3];
            (*video)["image"] = row[4];
            mysql_free_result(res);
            return true;
        }
        bool SelectLike(const std::string &key, Json::Value *videos)
        {
#define SELECTLIKE_VIDEO "select * from tb_video where name like '%%%s%%';"
            char sql[1024] = {0};
            sprintf(sql, SELECTLIKE_VIDEO, key.c_str());
            _mutex.lock(); //-----lock start 保护查询与保存结果到本地的过程
            bool ret = MysqlQuery(_mysql, sql);
            if (ret == false)
            {
                _mutex.unlock();
                return false;
            }
            MYSQL_RES *res = mysql_store_result(_mysql);
            if (res == NULL)
            {
                std::cout << "mysql store result failed!\n";
                _mutex.unlock();
                return false;
            }
            _mutex.unlock(); //------lock end
            int num_rows = mysql_num_rows(res);
            for (int i = 0; i < num_rows; i++)
            {
                MYSQL_ROW row = mysql_fetch_row(res);
                Json::Value video;
                video["id"] = atoi(row[0]);
                video["name"] = row[1];
                video["info"] = row[2];
                video["video"] = row[3];
                video["image"] = row[4];
                videos->append(video);
            }
            mysql_free_result(res);
            return true;
        }
    };
}

#endif

// #ifndef __MY_DATA__
// #define __MY_DATA__
// #include "util.hpp"
// #include <cstdlib>
// #include <mutex>
// #include <mysql/mysql.h>

// namespace aod
// {
// #define HOST "127.0.0.1"
// #define USER "root"
// #define PASS "20040905"
// #define NAME "aod_system"

//     // mysql 初始化和连接
//     //  mysql_init(NULL) 初始化一个 MySQL 连接句柄。
//     //  mysql_real_connect() 尝试与 MySQL 服务器建立连接。
//     //  mysql_set_character_set(mysql, "utf8") 设置连接字符集为 utf8，确保中文或其他字符集能够正确存储和检索。
//     //  如果连接失败，函数会输出错误信息并返回 NULL。
//     static MYSQL *MysqlInit()
//     {
//         MYSQL *mysql = mysql_init(NULL);
//         if (mysql == NULL)
//         {
//             std::cout << "init mysql instance failed!\n";
//             return NULL;
//         }
//         if (mysql_real_connect(mysql, HOST, USER, PASS, NAME, 0, NULL, 0) == NULL)
//         {
//             std::cout << "connect mysql server failed!\n";
//             mysql_close(mysql);
//             return NULL;
//         }
//         mysql_set_character_set(mysql, "utf8");
//         return mysql;
//     }

//     // mysql 销毁
//     //  mysql_close(mysql) 关闭 MySQL 连接。
//     //  如果传入的 mysql 句柄为空，则不进行任何操作。
//     static void MysqlDestroy(MYSQL *mysql)
//     {
//         if (mysql != NULL)
//         {
//             mysql_close(mysql);
//         }
//         return;
//     }

//     // 执行 mysql 的查询
//     //  mysql_query() 执行 SQL 查询。
//     //  如果执行失败，输出 SQL 错误信息和错误描述。
//     //  返回 true 表示查询成功，false 表示查询失败。
//     static bool MysqlQuery(MYSQL *mysql, const std::string &sql)
//     {
//         int ret = mysql_query(mysql, sql.c_str());
//         if (ret != 0)
//         {
//             std::cout << sql << std::endl;
//             std::cout << mysql_error(mysql) << std::endl;
//             return false;
//         }
//         return true;
//     }

//     // TableVideo 类
//     //  TableVideo 类封装了对 tb_video 表的操作。每个操作都对应一个数据库操作函数，如插入、更新、删除、查询等。
//     class TableVideo
//     {
//         // 成员变量
//         //  _mysql 是指向 MySQL 连接的指针，用于与数据库交互。
//         //  _mutex 是用于保证线程安全的互斥量，避免多线程环境下并发操作数据库时发生冲突。
//     private:
//         MYSQL *_mysql;
//         std::mutex _mutex;

//     public:
//         // 构造函数
//         // 构造函数初始化 _mysql，调用 MysqlInit() 来建立与数据库的连接。如果连接失败，则通过 exit(-1) 终止程序。
//         TableVideo()
//         {
//             _mysql = MysqlInit();
//             if (_mysql == NULL)
//             {
//                 exit(-1);
//             }
//         }

//         // 析构函数
//         // 析构函数确保在 TableVideo 对象销毁时关闭 MySQL 连接。
//         ~TableVideo()
//         {
//             MysqlDestroy(_mysql);
//         }

//         // 插入视频
//         // 插入一个视频记录到数据库中。
//         // 使用 sprintf 构建 SQL 插入语句。
//         // video["name"] 是视频的名称，video["info"] 是简介，video["video"] 是视频文件路径，video["image"] 是视频封面图片路径。
//         // 如果 name 字段为空，则返回 false。
//         // 最后调用 MysqlQuery 执行 SQL 查询。
//         bool Insert(const Json::Value &video)
//         {
//             // id name info video image
//             std::string sql;
//             sql.resize(4096 + video["info"].asString().size()); // 防止简介过长
// #define INSERT_VIDEO "insert tb_video values(null, '%s', '%s', '%s', '%s');"
//             if (video["name"].asString().size() == 0)
//             {
//                 return false;
//             }
//             // 要完成的细致的话需要对各个数据进行校验，因为不校验直接用就有可能出问题
//             // int sprintf(char *str, const char *format, ...);
//             // str：要存储格式化输出的目标字符串。
//             // format：格式化字符串，指定了如何插入其他参数（类似于 printf 函数）。
//             // ...：格式化字符串中的占位符需要的参数，通常是变量，它们会根据格式化字符串中的说明符插入到 str 中。
//             sprintf(&sql[0], INSERT_VIDEO, video["name"].asCString(),
//                     video["info"].asCString(),
//                     video["video"].asCString(),
//                     video["image"].asCString());
//             return MysqlQuery(_mysql, sql);
//         }

//         // 更新视频
//         // 更新视频的 name 和 info 字段，根据 video_id 确定要更新的记录。
//         bool Update(int video_id, const Json::Value &video)
//         {
//             std::string sql;
//             sql.resize(4096 + video["info"].asString().size()); // 防止简介过长
// #define UPDATE_VIDEO "update tb_video set name='%s', info='%s' where id=%d;"
//             sprintf(&sql[0], UPDATE_VIDEO, video["name"].asCString(),
//                     video["info"].asCString(), video_id);
//             return MysqlQuery(_mysql, sql);
//         }

//         // 删除视频
//         // 删除指定 video_id 的视频记录。
//         bool Delete(int video_id)
//         {
// #define DELETE_VIDEO "delete from tb_video where id=%d;"
//             char sql[1024] = {0};
//             sprintf(sql, DELETE_VIDEO, video_id);
//             return MysqlQuery(_mysql, sql);
//         }

//         // 查看所有视频
//         // SelectAll 查询所有视频信息，并将结果保存到 videos 中。
//         // 使用 mysql_store_result() 获取查询结果。
//         // 在查询过程中使用互斥量 _mutex 来保护数据库操作，确保线程安全。
//         bool SelectAll(Json::Value *videos)
//         {
// #define SELECTALL_VIDEO "select * from tb_video;"
//             _mutex.lock(); //-----lock start 保护查询与保存结果到本地的过程
//             bool ret = MysqlQuery(_mysql, SELECTALL_VIDEO);
//             if (ret == false)
//             {
//                 _mutex.unlock();
//                 return false;
//             }
//             MYSQL_RES *res = mysql_store_result(_mysql);
//             if (res == NULL)
//             {
//                 std::cout << "mysql store result failed!\n";
//                 _mutex.unlock();
//                 return false;
//             }
//             _mutex.unlock(); //------lock end

//             int num_rows = mysql_num_rows(res);
//             for (int i = 0; i < num_rows; i++)
//             {
//                 MYSQL_ROW row = mysql_fetch_row(res);
//                 Json::Value video;
//                 video["id"] = atoi(row[0]);
//                 video["name"] = row[1];
//                 video["info"] = row[2];
//                 video["video"] = row[3];
//                 video["image"] = row[4];
//                 videos->append(video);
//             }
//             mysql_free_result(res);
//             return true;
//         }

//         // 查询单个视频
//         // 根据 video_id 查询单个视频的信息，并将结果存储在 video 中。
//         bool SelectOne(int video_id, Json::Value *video)
//         {
// #define SELECTONE_VIDEO "select * from tb_video where id=%d;"
//             char sql[1024] = {0};
//             sprintf(sql, SELECTONE_VIDEO, video_id);
//             _mutex.lock(); //-----lock start 保护查询与保存结果到本地的过程
//             bool ret = MysqlQuery(_mysql, sql);
//             if (ret == false)
//             {
//                 _mutex.unlock();
//                 return false;
//             }
//             MYSQL_RES *res = mysql_store_result(_mysql);
//             if (res == NULL)
//             {
//                 std::cout << "mysql store result failed!\n";
//                 _mutex.unlock();
//                 return false;
//             }
//             _mutex.unlock(); //------lock end
//             int num_rows = mysql_num_rows(res);
//             if (num_rows != 1)
//             {
//                 std::cout << "have no data!\n";
//                 mysql_free_result(res);
//                 return false;
//             }
//             MYSQL_ROW row = mysql_fetch_row(res);
//             (*video)["id"] = video_id;
//             (*video)["name"] = row[1];
//             (*video)["info"] = row[2];
//             (*video)["video"] = row[3];
//             (*video)["image"] = row[4];
//             mysql_free_result(res);
//             return true;
//         }

//         // 模糊查询
//         // 根据 key（关键字）模糊查询视频名称，并将符合条件的视频信息保存到 videos 中。
//         bool SelectLike(const std::string &key, Json::Value *videos)
//         {
// #define SELECTLIKE_VIDEO "select * from tb_video where name like '%%%s%%';"
//             // like '%%%s%%': 这是一个模式匹配条件，用来查找包含特定字符串的记录。
//             // like: SQL 中用于进行模糊匹配。它支持使用通配符来进行部分匹配。
//             // %%%s%%: 这是一个格式化的字符串，%s 是一个占位符，表示查询时将插入实际的字符串。它两边的 % 是通配符，表示在 name 字段中，目标字符串可以出现在任何位置。
//             // 第一个 % 和最后一个 % 是 SQL 中的通配符，表示任意数量的字符（包括零个字符），即它会匹配所有包含目标字符串的 name 值。
//             // %s 是格式化字符串占位符，最终会被你实际提供的查询条件替代。

//             char sql[1024] = {0};
//             sprintf(sql, SELECTLIKE_VIDEO, key.c_str());
//             _mutex.lock(); //-----lock start 保护查询与保存结果到本地的过程
//             bool ret = MysqlQuery(_mysql, sql);
//             if (ret == false)
//             {
//                 _mutex.unlock();
//                 return false;
//             }
//             MYSQL_RES *res = mysql_store_result(_mysql);
//             if (res == NULL)
//             {
//                 std::cout << "mysql store result failed!\n";
//                 _mutex.unlock();
//                 return false;
//             }
//             _mutex.unlock(); //------lock end
//             int num_rows = mysql_num_rows(res);
//             for (int i = 0; i < num_rows; i++)
//             {
//                 MYSQL_ROW row = mysql_fetch_row(res);
//                 Json::Value video;
//                 video["id"] = atoi(row[0]);
//                 video["name"] = row[1];
//                 video["info"] = row[2];
//                 video["video"] = row[3];
//                 video["image"] = row[4];
//                 videos->append(video);
//             }
//             mysql_free_result(res);
//             return true;
//         }
//     };
// }

// #endif

// UPDATE mysql.user SET authentication_string=PASSWORD('20040905') WHERE user='root' AND host='localhost';
// FLUSH PRIVILEGES;

// GRANT ALL PRIVILEGES ON aod_system.* TO 'root'@'localhost' IDENTIFIED BY '';
// FLUSH PRIVILEGES;


// 为什么使用静态方法 :
// 1. 这些方法不依赖于类的实例 :
// 静态方法不依赖于类的实例化，也就是说，它们可以在没有创建类对象的情况下直接被调用。在你的代码中，MysqlInit()、MysqlDestroy() 和 MysqlQuery() 这三个方法并不操作类的成员变量或成员函数，它们只是与 MySQL 数据库进行交互。这些方法所需的状态完全可以通过传递给它们的参数来管理（比如 MYSQL *mysql）。
// 因为这些方法不依赖于类的成员（没有 this 指针），它们可以作为静态方法被直接调用，而不需要创建类的实例。

// 2. 避免实例化对象的开销
// 静态方法是属于类本身的，而不是类的对象。在一些情况下，创建一个类实例是没有必要的，尤其是像这个 TableVideo 类中，很多操作（如数据库连接和查询）不需要保持在对象的生命周期内。这些方法本身不维护任何对象的状态，仅仅是与外部的 MySQL 数据库进行交互。因此，将它们定义为静态方法可以减少不必要的实例化开销。
// 如果不使用静态方法，想要调用这些方法，你就必须首先创建 TableVideo 类的一个对象，造成不必要的对象创建和销毁：
// 使用静态方法后，可以直接通过类名调用，不需要创建对象，代码更加简洁高效。

// 3. 代码逻辑与数据库操作独立
// 通过将这些与数据库操作相关的方法定义为静态方法，你能够将它们的功能与类的其他部分（如视频数据管理的逻辑）分离开来。这使得 TableVideo 类的主要职责更加清晰：它的主要目标是封装视频数据的操作（如插入、更新、删除、查询等），而 MySQL 连接和操作则通过静态方法进行封装。
// 这样设计有利于代码的模块化，使得数据库的初始化、查询等功能能够独立使用，也能在不创建 TableVideo 对象的情况下使用这些数据库操作方法。

// 4. 更容易进行全局使用
// 静态方法可以在任何地方调用，而不需要依赖于具体的类实例。这样，如果你需要在程序的多个地方进行数据库操作，直接调用这些静态方法会非常方便。比如，在项目中的其他类中也可以调用 MysqlInit()、MysqlQuery() 和 MysqlDestroy()，而不需要将这些方法引入到其他类实例中，或者传递实例化的对象。

// 将这三个方法定义为静态方法的主要原因是它们与 TableVideo 类的实例状态无关。它们只是提供了与 MySQL 进行交互的工具方法，封装了数据库的初始化、销毁和查询操作。由于不需要依赖于类实例，也不需要维护类的成员状态，使用静态方法能够简化代码并提高效率。